#pragma once

namespace dotNetInteropPlugin {
	namespace PluginDev {
		using namespace System;
		using namespace System::Runtime::InteropServices;
		using namespace System::Drawing;


		typedef void (__stdcall UnmanagedAction)();

		class UnamangedMenuHelper
		{
		public:
			UnamangedMenuHelper(IAIMP2Controller* ctrl, Action^ dlg)
			{
				AIMPCtrl = ctrl;
				AIMPCtrl->AddRef();
				callDelegate = dlg;

				callDelegatePtr = (UnmanagedAction*)(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(dlg).ToPointer());
			}


			void FillData(AIMPMenuInfo& menuInf)
			{
				menuInf.ProcPtr = &MenuCallbackFnc;
				menuInf.User = (DWORD)this;
			}

			~UnamangedMenuHelper()
			{
				if (AIMPCtrl != NULL)
				{
					AIMPCtrl->Release();
					AIMPCtrl = NULL;
				}
				callDelegate = nullptr;
			}

			IAIMP2Controller* AController()
			{
				return AIMPCtrl;
			}

		private:
			IAIMP2Controller* AIMPCtrl;
			gcroot<Action^> callDelegate;
			UnmanagedAction* callDelegatePtr;

			static void WINAPI MenuCallbackFnc(DWORD User, void* Handle)
			{
				UnamangedMenuHelper* cbTp = (UnamangedMenuHelper*)User;
				(*cbTp).callDelegatePtr();
			}
		};


		/// <summary>
		/// Represent menu items for AIMP
		/// </summary>
		public ref class MAIMPMenuItem sealed: public MAIMPMenuItemBase
		{
		public:
			/// <summary>
			/// Creates new menu item
			/// </summary>
			MAIMPMenuItem()
			{
				RegMenuHandler = 0;
				inHelper = NULL;
				inSubMenuItmes = gcnew MAIMPMenuItemCollection(this);
			}

			/// <summary>
			/// Create new menu item with particular text
			/// </summary>
			/// <param name="MenuText">MenuItem text</param>
			MAIMPMenuItem(String^ MenuText)
			{
				RegMenuHandler = 0;
				inHelper = NULL;
				inSubMenuItmes = gcnew MAIMPMenuItemCollection(this);
				this->Text = MenuText;
			}

			/// <summary>
			/// Copy constructor
			/// </summary>
			/// <param name="Src">Source MenuItem</param>
			MAIMPMenuItem(MAIMPMenuItem^ Src): MAIMPMenuItemBase(Src)
			{
				RegMenuHandler = 0;
				inHelper = NULL;

				inSubMenuItmes = gcnew MAIMPMenuItemCollection(this);

				for (int i = 0; i < Src->SubMenuItmes->Count; i++)
					inSubMenuItmes->Add(Src->SubMenuItmes->default[i]->GetCopy());
			}

			/// <summary>
			/// Get copy of menu item
			/// </summary>
			/// <returns>Copy of menu item</returns>
			MAIMPMenuItem^ GetCopy()
			{
				return gcnew MAIMPMenuItem(this);
			}


		internal:
			virtual Boolean UpdateData() override
			{
				Boolean Res = false;
				if (inHelper != NULL)
				{
					AIMPMenuInfo AddMenuInf;

					FillDataWithoutText(AddMenuInf);
					inHelper->FillData(AddMenuInf);

					pin_ptr<const WCHAR> strData = PtrToStringChars(this->Text);
					AddMenuInf.Caption = (PWCHAR)strData;

					Res = inHelper->AController()->AIMP_Menu_Update(RegMenuHandler, &AddMenuInf);
				}
				return Res;
			}

		internal:
			Boolean Register(IAIMP2Controller* ctrl, AIMPPlayerMenu whereReg)
			{
				Boolean Res = false;
				if (inHelper == NULL)
				{
					AIMPMenuInfo AddMenuInf;

					inHelper = new UnamangedMenuHelper(ctrl, gcnew Action(this, &MAIMPMenuItem::OnClick));

					FillDataWithoutText(AddMenuInf);
					inHelper->FillData(AddMenuInf);

					pin_ptr<const WCHAR> strData = PtrToStringChars(this->Text);
					AddMenuInf.Caption = (PWCHAR)strData;

					RegMenuHandler = ctrl->AIMP_Menu_Create((DWORD)whereReg, &AddMenuInf);

					Res = (RegMenuHandler != 0);

					if (RegMenuHandler == 0)
					{
						delete inHelper;
						inHelper = NULL;
					}
					else
					{
						for (int i = 0; i < SubMenuItmes->Count; i++)
							SubMenuItmes->default[i]->RegisterSubMenu(ctrl, RegMenuHandler);
					}
					
				}
				return Res;
			}


			Boolean RegisterSubMenu(IAIMP2Controller* ctrl, DWORD ParentHandle)
			{
				Boolean Res = false;
				if (inHelper == NULL)
				{
					AIMPMenuInfo AddMenuInf;

					inHelper = new UnamangedMenuHelper(ctrl, gcnew Action(this, &MAIMPMenuItem::OnClick));

					FillDataWithoutText(AddMenuInf);
					inHelper->FillData(AddMenuInf);

					pin_ptr<const WCHAR> strData = PtrToStringChars(this->Text);
					AddMenuInf.Caption = (PWCHAR)strData;

					RegMenuHandler = ctrl->AIMP_Menu_CreateEx(ParentHandle, &AddMenuInf);

					Res = (RegMenuHandler != 0);

					if (RegMenuHandler == 0)
					{
						delete inHelper;
						inHelper = NULL;
					}
					else
					{
						for (int i = 0; i < SubMenuItmes->Count; i++)
							SubMenuItmes->default[i]->RegisterSubMenu(ctrl, RegMenuHandler);
					}

				}
				return Res;
			}

			Boolean Unregister()
			{
				Boolean Res = false;
				if (inHelper != NULL)
				{
					for (int i = 0; i < SubMenuItmes->Count; i++)
						SubMenuItmes[i]->Unregister();
					Res = inHelper->AController()->AIMP_Menu_Remove(RegMenuHandler);
					delete inHelper;
					inHelper = NULL;
				}
				return Res;
			}

		private:
			DWORD RegMenuHandler;
			UnamangedMenuHelper* inHelper;

			void FillDataWithoutText(AIMPMenuInfo& mi)
			{
				switch (this->ItemType)
				{
				case AIMPMenuItemType::Normal:
					mi.Checkbox = false;
					mi.Checked = false;
					mi.RadioItem = false;
					break;
				case AIMPMenuItemType::CheckBox:
					mi.Checkbox = true;
					mi.Checked = this->IsChecked;
					mi.RadioItem =false;
					break;
				case AIMPMenuItemType::RadioButton:
					mi.Checkbox = false;
					mi.Checked = this->IsChecked;
					mi.RadioItem = true;
					break;
				}

				mi.Enabled = this->IsEnabled;
				
				if (BmpImage != nullptr)
				{
					mi.Bitmap = (HBITMAP)this->BmpImage->GetHbitmap().ToPointer();
				}
				else
				{
					mi.Bitmap = NULL;
				}
			}


		public:
			ref class MAIMPMenuItemCollection sealed: dotNetInteropPlugin::PluginDev::Additions::MBRCollectionNI<MAIMPMenuItem^>
			{
			internal:
				MAIMPMenuItem^ inParent;
				MAIMPMenuItemCollection(MAIMPMenuItem^ prnt)
				{
					inParent = prnt;
				}
			protected:
				virtual void InsertItem(Int32 index, MAIMPMenuItem^ val) override
				{
					bool Continue = true;

					if (inParent->inHelper != NULL)
						Continue = val->RegisterSubMenu(inParent->inHelper->AController(), inParent->RegMenuHandler);

					if (Continue)
						dotNetInteropPlugin::PluginDev::Additions::MBRCollectionNI<MAIMPMenuItem^>::InsertItem(index, val);
				}

				virtual void ClearItems() override
				{				
					for (int index = 0; index < this->Count; index++)
						this->default[index]->Unregister();
					dotNetInteropPlugin::PluginDev::Additions::MBRCollectionNI<MAIMPMenuItem^>::ClearItems();
				}
				virtual void RemoveItem(Int32 index) override
				{
					if (index > 0 && index < this->Count)
						this->default[index]->Unregister();
					dotNetInteropPlugin::PluginDev::Additions::MBRCollectionNI<MAIMPMenuItem^>::RemoveItem(index);
				}
			};


		public:
			/// <summary>
			/// Submenu
			/// </summary>
			property MAIMPMenuItemCollection^ SubMenuItmes
			{
				MAIMPMenuItemCollection^ get()
				{
					return inSubMenuItmes;
				}
			}
		private:
			MAIMPMenuItemCollection^ inSubMenuItmes;
		};

	}
}